#ifndef __WORKER_HPP__
#define __WORKER_HPP__

#include <stdio.h>
#include <stdlib.h>

#include "../common/Math.h"
#include "../common/Helpers.hpp"
#include "../support/Threading.hpp"

#include "SWARMDeps.hpp"
#include "SWARMTaskTag.hpp"
#include "SWARMTaskTagWrapper.hpp"
#include "Externs.hpp"


struct computePreds {
  // condsE must be a tuple holding <zero, false, ... false>
  template<int TASK_ID, int TASKTAGDIM, int INTERTASKDIM, typename... BOOLS, typename... DEPDISTS>
  static void compute(SwarmTaskTag<TASK_ID, TASKTAGDIM, INTERTASKDIM> const & tt,
                      std::tuple<BOOLS...> &condsE, std::tuple<DEPDISTS...> const &dists) {
    // static_assert(tuple_size);
    constexpr int NPARAMS = TASKTAGDIM - INTERTASKDIM;
    
    // Tuple holding <One, t1 ... t_INTERTASKDIM, p1, ... p_NPARAMS>
    static constexpr auto termsE = 
      std::tuple_cat(make_tuple(ConstantTerm()), 
                     makeCompositeTuple<VariableTerm, INTERTASKDIM+NPARAMS>::make());
    // //
    // /////////////////////////////////////////////////////////////////////////////////    
    // // Still TBD how to compress this even more with templates
    // // Start autogen
    // static constexpr auto t1 = std::get<1>(termsE);
    // static constexpr auto t2 = std::get<2>(termsE);
    // static constexpr auto t3 = std::get<3>(termsE);
    // static constexpr auto pT = std::get<1+INTERTASKDIM>(termsE);
    // // static constexpr auto pM = std::get<2+INTERTASKDIM>(termsE);
    // static constexpr auto pN = std::get<3+INTERTASKDIM>(termsE);
    // // static constexpr auto pP = std::get<4+INTERTASKDIM>(termsE);
    
    // static constexpr auto p1 = 
    //   FOR(t1, CEIL(-pN-15,16), FLOOR(pT-3,16));
    // static constexpr auto p2 = 
    //   FOR(t2, MAX(t1,-t1-1), 
    //       MIN(MIN(FLOOR(-8*t1+pT-2,8),FLOOR(8*t1+pN+7,8)),FLOOR(pT+pN-2,16)));
    // static constexpr auto p3 = 
    //   FOR (t3, MAX(MAX(0,CEIL(t1+t2-1,2)),CEIL(16*t2-pN-14,16)), 
    //        MIN(MIN(FLOOR(pT+pN-2,16),FLOOR(16*t2+pN+14,16)),FLOOR(8*t1+8*t2+pN+15,16)));

    // static constexpr auto lbsE = 
    //   std::make_tuple(ConstantTerm(), 
    //                   (t1 >= p1.first), (t2 >= p2.first), (t3 >= p3.first));
    // static constexpr auto ubsE = 
    //   std::make_tuple(ConstantTerm(), 
    //                   (t1 <= p1.second), (t2 <= p2.second), t3 <= (p3.second));

    // // END Autogen
    // // END Still TBD how to compress this even more with templates
    // /////////////////////////////////////////////////////////////////////////////////  
    // //

#define __DEP_ET_DEF__
#include "AutoGenerated.rstream.hpp"
#undef __DEP_ET_DEF__

    // tuple holding <1, tt[0], ... tt[INTERTASKDIM+NPARAMS-1]>
    auto tagAsTupleE = std::tuple_cat(make_tuple(1), tt.toTuple());

    computePredsHelper<TASKTAGDIM, INTERTASKDIM, 1, INTERTASKDIM>::compute
      (condsE, dists, lbsE, ubsE, tagAsTupleE);
  }
};




template<typename THISDECL, typename CODELETDECL, typename SWARM_TAGTABLE, 
         int TASK_ID, int TASKTAGDIM, int INTERTASKDIM>
bool asyncGets(THISDECL THIS, CODELETDECL &CODELET, SWARM_TAGTABLE &stt, 
              const SwarmTaskTag<TASK_ID, TASKTAGDIM, INTERTASKDIM> &this_tag) {

#ifdef MEASURE_OVERHEADS
  Timer oh(GlobalTimers::Singleton().getOverheadGlobal());
#endif

  {
    // // Start autogen
    // // Dependence specific
    // // static constexpr tag_elem_t d1 = 1, d2 = 1, d3 = 1;
    // static constexpr auto dists = 
    //   std::make_tuple(ConstantTerm(), (ConstantTerm)1, (ConstantTerm)1, (ConstantTerm)1);
    // // End autogen

#define __DEP_DIST_DEF__
#define __DEP_DIST_NO_DEF__
#include "AutoGenerated.rstream.hpp"
#undef __DEP_DIST_NO_DEF__
#undef __DEP_DIST_DEF__


    // SWARM-specific
    // Need a const_cast to modify the Tag held by THIS
    SwarmTaskTag<TASK_ID, TASKTAGDIM, INTERTASKDIM> &tt = 
      const_cast<SwarmTaskTag<TASK_ID, TASKTAGDIM, INTERTASKDIM> &>(this_tag);

    SWARMRuntimeWrapper<THISDECL, CODELETDECL, SWARM_TAGTABLE> 
      W(THIS, CODELET, stt);

    auto condsE = makeIdenticalTuple<bool, INTERTASKDIM+1>::make(false);
    computePreds::compute<TASK_ID, TASKTAGDIM, INTERTASKDIM> 
      (tt, condsE, dists);

    void * dummy;
    SWARMDeps<TASK_ID, TASKTAGDIM, INTERTASKDIM>::genGets(W, tt, condsE, dists, dummy);

#ifdef DEBUG
    SEQUENTIAL_DUMP1("Suspend = " << W.markedAsyncGet() << " \n");
#endif

    if (W.markedAsyncGet()) return true;
  }

  return false;
}

#endif // __WORKER_HPP__
